﻿using System.Globalization;
using fCloud.SensorApi.Models;
using InfluxDB.Client.Writes;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System.Linq;
using fCloud.SensorApi.Models.Dto;

namespace fCloud.SensorApi.Services;

public class TaskSchedulerService :ITaskSchedulerService
{
    private List<AlarmData> _alarmList = [];
    private List<Sensor> _sensorList = [];
    private List<NodeData>? _mqttServerSensors = [];
    private List<Device> _deviceList = [];
    
    private readonly IMysqlDbService mysqlDbService;
    private readonly IReceiverService receiverService;
    private readonly IHttpService httpService;
    private readonly IConfiguration config;
    private readonly IRedisService redisService;
    private readonly IInfluxDbService influxDbService;
    private readonly IServiceScopeFactory serviceScopeFactory;

    public TaskSchedulerService( 
        IReceiverService _receiverService,
        IHttpService _httpService,
        IConfiguration _config,
        IRedisService _redisService,
        IInfluxDbService _influxDbService,
        IServiceScopeFactory _serviceScopeFactory)
    {
        serviceScopeFactory = _serviceScopeFactory;
        receiverService = _receiverService;
        httpService = _httpService;
        config = _config;
        redisService = _redisService;
        influxDbService = _influxDbService;
        mysqlDbService=serviceScopeFactory.CreateScope().ServiceProvider.GetRequiredService<IMysqlDbService>();
    }

    //获取mqtt服务器上的传感器列表
    private async Task CollectionDbInfo()
    {
        _alarmList =await GetAlarmList();
        await Task.Delay(500);
        _mqttServerSensors=await GetMqttServerSensors();
        _sensorList=await GetSensorList();
        await Task.Delay(500);
        _deviceList=await GetDeviceList();
        await Task.Delay(500);
    }

    private async Task<List<NodeData>?> GetMqttServerSensors()
    {
        var baseurl = config["Emqx:Url"];
        var clientUrl = baseurl+"/clients";
        var result = await httpService.GetMqttClientsAsync(clientUrl);
        var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(result);
        var clientList = apiResponse?.Data
            .Where(client =>
                client.ClientId.StartsWith("D", StringComparison.OrdinalIgnoreCase) ||
                client.ClientId.StartsWith("Y", StringComparison.OrdinalIgnoreCase) ||
                client.ClientId.StartsWith("ZX", StringComparison.OrdinalIgnoreCase))
            .ToList();
        return clientList;
    }
    //获取mysql数据库中报警列表
    private async Task<List<AlarmData>> GetAlarmList()
    {
        return await mysqlDbService.GetAlarmData();
    }

    //获取数据库传感器列表
    private async Task<List<Sensor>> GetSensorList()
    {
        return await mysqlDbService.GetSensors();
    }
    public async Task<List<Device>> GetDeviceList()
    {
        try
        {
            var res = await mysqlDbService.GetDevices();
            return res;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }

        return null;
    }
    /// <summary>
    /// 接口返回给前端设备管理模块
    /// </summary>
    /// <returns></returns>
    public async Task<MqttSensorsApiResponse> MergeSensorList()
    {
        await CollectionDbInfo();
        var mergedList = (
                from sensor in _sensorList
                join mqtt in _mqttServerSensors 
                    on sensor.SensorCode equals mqtt.ClientId into gj
                from mqtt in gj.DefaultIfEmpty()
                select new NodeData
                {
                    ClientId = sensor.SensorCode,
                    Connected = mqtt?.Connected ?? false,
                })
            .Union(
                from mqtt in _mqttServerSensors
                where _sensorList.All(s => s.SensorCode != mqtt.ClientId)
                select new NodeData
                {
                    ClientId = mqtt.ClientId,
                    Connected = mqtt.Connected
                })
            .ToList();
        var totalCount = mergedList.Count;
        
        var onlineCount = mergedList.Count(c => c.Connected);
        var onlineDevices =(onlineCount>=10)? 
            mergedList.Where(c => c.Connected).ToList().Take(10): 
            mergedList.Where(c => c.Connected).ToList();
        
        var onlineClients = onlineDevices.Select(c => new OnlineClientInfo
        {
            ClientId = c.ClientId,
            OnlineHours = new Random().Next(1000,1500),
            OfflineHours = new Random().Next(100,200),
            WarningCount = _alarmList.Where(a=>a.SensorSn == c.ClientId).ToList().Count
        }).ToList();
        var mqttSensorsApiResponse= new MqttSensorsData()
        {
            Clients = mergedList,
            Count = new CountInfo
            {
                TotalCount = totalCount,
                OnlineCount = onlineCount,
                OfflineCount = totalCount - onlineCount,
                FaltCount = _alarmList.Count
            },
            OnlineClients = onlineClients
        };
        return new MqttSensorsApiResponse()
        {
            Code = 200,
            Message = "success",
            Data = mqttSensorsApiResponse
        };
    }
    public async Task<CommonApiResponse> GetAlarmShowInfoList()
    {
        var alarmList = await GetAlarmList();
        var orderedAlarmList = alarmList .OrderByDescending(i => i.WarnTime).ToList();
        var alarmShowInfos = orderedAlarmList.Select(i => new AlarmShowInfo
        {
            WarnTime = i.WarnTime.ToString("yyyy-MM-dd HH:mm:ss"),
            DeviceName = i.DeviceName,
            AlarmThreshold = i.ParameterCode,
            AlarmValue = i.Value,
            Value = i.Ppm
        }).ToList();
        var apiResponse = new CommonApiResponse()
        {
            Code = 200,
            Message = "success",
            Data = alarmShowInfos
        };
        return apiResponse;
    }

    public async Task<CommonApiResponse> GetSensorDetails()
    {
        await CollectionDbInfo();
        //var devices = await mysqlDbService.GetDevices();
            var devicesDto =  _deviceList
            .Select(d => new DeviceDto
            {
                Name = d.Name,
                TName = d.TName,
                Location = d.Location,
                Status = d.Status
            }).ToList();

        //var sensors = await mysqlDbService.GetSensors();
            var sensorsDto = _sensorList.Select(s => new SensorDto
            {
                SensorCode = s.SensorCode,
                SensorType = s.SensorType,
                SensorMode = s.SensorMode,
                GatewayId = s.GatewayId
            }).ToList();
        var realTimeSensors = await redisService.ScanSensorKeysAsync();

        var deviceRealData = (
            from device in devicesDto

            // Join: device.TName -> sensor.SensorCode
            join sensor in sensorsDto
                on device.TName equals sensor.SensorCode into sensorGroup
            from rtSensorInfo in sensorGroup.DefaultIfEmpty()       // 左连接 Sensors

            // Join: device.TName -> realTimeSensors.SensorSn
            join rtSensor in realTimeSensors
                on device.TName equals rtSensor.SensorSn into rtGroup
            from sensorNew in rtGroup.DefaultIfEmpty()    
            select new 
            {
                Name = device.Name,
                TName = device.TName,
                Location = device.Location,
                Status = device.Status,

                SensorType = rtSensorInfo?.SensorType,
                SensorMode = rtSensorInfo?.SensorMode,

                Temper = sensorNew?.Temper,
                Dens = sensorNew?.Dens,
                Vis = sensorNew?.Vis,
                Vis40 = sensorNew?.Vis40,
                Diel = sensorNew?.Diel,
                Ppm = sensorNew?.Ppm,
                Aw = sensorNew?.Aw,
                Mo = sensorNew?.Mo,
                Par1 = sensorNew?.Par1,
                Par2 = sensorNew?.Par2,
                Par3 = sensorNew?.Par3,
                Par4 = sensorNew?.Par4,
                Par5 = sensorNew?.Par5,
                Par6 = sensorNew?.Par6,
                Par7 = sensorNew?.Par7,
                Par8 = sensorNew?.Par8,
                Class1 = sensorNew?.Class1,
                Class2 = sensorNew?.Class2,
                Class3 = sensorNew?.Class3,
                Class4 = sensorNew?.Class4,
                Class5 = sensorNew?.Class5,
                Class6 = sensorNew?.Class6,
                ClassTotal = sensorNew?.ClassTotal,
                Standard = sensorNew?.Standard,
                Fe1 = sensorNew?.Fe1,
                Fe2 = sensorNew?.Fe2,
                Fe3 = sensorNew?.Fe3,
                Fe4 = sensorNew?.Fe4,
                Fe5 = sensorNew?.Fe5,
                Fe6 = sensorNew?.Fe6,
                Fe7 = sensorNew?.Fe7,
                Fe8 = sensorNew?.Fe8,
                NFe1 = sensorNew?.NFe1,
                NFe2 = sensorNew?.NFe2,
                NFe3 = sensorNew?.NFe3,
                NFe4 = sensorNew?.NFe4,
                NFe5 = sensorNew?.NFe5,
                Fe = sensorNew?.Fe,
                NFe = sensorNew?.NFe,
                State = sensorNew?.State,
                Press = sensorNew?.Press,
                Speed = sensorNew?.Speed
            }
        ).ToList();
        
       
        var devicesApiResponse = new CommonApiResponse()
        {
            Code = 200,
            Message = "success",
            Data = deviceRealData
        };
        return devicesApiResponse;
    }

    public async Task<CommonApiResponse> ScanSensorKeysAsync()
    {
        var sensorNewResponse = await redisService.ScanSensorKeysAsync();
        var list = sensorNewResponse
            .OrderByDescending(s => s.CreateTime)
            .Take(6)
            .ToList();
        return new CommonApiResponse()
        {
            Code = 200,
            Message = "success",
            Data = list
        };
    }
    public async Task<CommonApiResponse> GetSensorBySensorSn(string sensorSn)
    {
        //var sensor = await dbContext.Sensors.FirstOrDefaultAsync(s => s.SensorCode == sensorSn);
        var realtimeSensor = await redisService.ScanSensorKeysAsync();
        var selectedSensor = realtimeSensor.FirstOrDefault(c => c.SensorSn == sensorSn);
        return new CommonApiResponse()
        {
            Code = 200,
            Message = "success",
            Data = selectedSensor
        };
    }

    public async Task<SensorHistoryDto> GetPointsAsync(
        string measurement, 
        string tag,
        string field,
        DateTime? startTime = null,
        DateTime? endTime = null)
    {
        return await influxDbService.GetPointsAsync(measurement,tag,field);
    }
    
    public async Task<PagedCommonApiResponse<SensorData>> GetSensorDataBySensorSnWithPaging(
        string sensorSn,
        int page = 1,
        int pageSize = 10)
    {
        try
        {
            if (page < 1) page = 1;
            if (pageSize < 1 || pageSize > 100) pageSize = 10;

            var totalCount = await mysqlDbService.CountSensorDataBySensorSnAsync(sensorSn);
            var data = await mysqlDbService.GetSensorDataBySensorSnAsync(sensorSn, page, pageSize);

            var result = new PagedResult<SensorData>
            {
                Data = data,
                TotalCount = totalCount,
                PageNumber = page,
                PageSize = pageSize
            };

            return new PagedCommonApiResponse<SensorData>
            {
                Code = 200,
                Message = "success",
                Data = result
            };
        }
        catch (Exception ex)
        {
            return new PagedCommonApiResponse<SensorData>
            {
                Code = 500,
                Message = $"Internal server error: {ex.Message}",
                Data = null
            };
        }
    }

}
public interface ITaskSchedulerService
{
    public Task<MqttSensorsApiResponse> MergeSensorList();
    public Task<CommonApiResponse> GetAlarmShowInfoList();
    
    public Task<CommonApiResponse> GetSensorDetails();
    public Task<CommonApiResponse> ScanSensorKeysAsync();
    public Task<CommonApiResponse> GetSensorBySensorSn(string sensorSn);

    public Task<SensorHistoryDto> GetPointsAsync(
        string measurement,
        string tag,
        string field,
        DateTime? startTime = null,
        DateTime? endTime = null);

    public Task<PagedCommonApiResponse<SensorData>> GetSensorDataBySensorSnWithPaging(
        string sensorSn,
        int page = 1,
        int pageSize = 10);

    public  Task<List<Device>> GetDeviceList();
}